`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2024/03/11 21:20:00
// Design Name: 
// Module Name: arp_rx
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

/*
    逻辑没问题，总体思路清晰，但是写完脑子有点昏，可能会出现些许纰漏，待后面仿真验证再完善
*/

/*
接收数据参考时钟，1000Mbps 速率下，时钟频率为125MHz，时钟为上下沿同时采样；
100Mbps 速率下，时钟频率为25MHz；10Mbps 速率下，时钟频率为2.5MHz，ETH_RXC 由PHY 侧提供
*/
// arp_rx是一个拆解以太网帧的模块
module arp_rx#(
   //开发板MAC地址 00-11-22-33-44-55
   parameter BOARD_MAC = 48'h00_11_22_33_44_55,  
   //开发板IP地址 192.168.1.10   
   parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10},
   parameter DSE_IP = {8'd192,8'd168,8'd1,8'd102}
)(  
    input                               rst_n           ,   
    input                               gmii_rx_clk     ,   // gmii读时钟
    (*IOB = "TRUE"*)input               gmii_rx_en_i    ,   // gmii读使能，增强时序稳定性
    (*IOB = "TRUE"*)input       [7:0]   gmii_rx_data_i  ,   // gmii读数据
    
    output reg                  [47:0]  des_mac         ,   // 源mac
    output reg                  [31:0]  des_ip          ,   // 源ip
    output reg                          rx_done         ,   // arp执行结束
    output reg                          type                // arp请求/应答     请求：1   应答：0  
);

// reg define
reg [6:0]   cnt                     ;   // 帧计时
reg [3:0]   cur_state, next_state   ;
reg         skip_en                 ;   // 状态跳转信号
reg         error_en                ;   // 错误信号标志

reg [47:0]  eth_des_mac_t           ;   // 在以太网帧里面的目的mac缓存
reg [15:0]  type_t                  ;   // 在以太网帧里面的类型

     
reg [15:0]  op_data                 ;   // 在arp帧里面的操作码
reg [47:0]  data_source_mac_t       ;   // 在arp帧里面的源mac缓存
reg [31:0]  data_source_ip_t        ;   // 在arp帧里面的源ip缓存
reg [31:0]  data_des_ip_t           ;   // 在arp帧里面的目的ip缓存

reg         gmii_rx_dv_reg;
reg [7:0]   gmii_rx_data_reg;
reg         rx_dv_dly1;
reg         gmii_rx_en;
reg [7:0]   rx_data_dly1;
reg [7:0]   gmii_rx_data;

// wire define
wire clk;

localparam 
            IDLE        = 4'd0,
            PRE         = 4'd1,
            SFD         = 4'd2,
            DES_MAC     = 4'd3,
            SOURCE_MAC  = 4'd4,
            TYPE        = 4'd5,
            DATA        = 4'd6,
            FCS         = 4'd7,
            ERR         = 4'd8;

/*****************************************************************
                            main code                           
*****************************************************************/
assign clk = gmii_rx_clk;

// 寄存输入信号并打两拍使用
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)begin
        gmii_rx_dv_reg <= 1'd0;
        gmii_rx_data_reg <= 8'd0;
    end
    else begin
        gmii_rx_dv_reg <= gmii_rx_en_i;
        gmii_rx_data_reg <= gmii_rx_data_i;
    end
end

always @(posedge clk or negedge rst_n) begin
    if(~rst_n)begin
        rx_dv_dly1   <= 1'd0;
        gmii_rx_en   <= 1'd0;
        rx_data_dly1 <= 8'd0;
        gmii_rx_data <= 8'd0;
    end
    else begin
        rx_dv_dly1 <= gmii_rx_dv_reg;
        gmii_rx_en <= rx_dv_dly1;
        rx_data_dly1 <= gmii_rx_data_reg;
        gmii_rx_data <= rx_data_dly1;
    end
end

// 三段式状态地第一阶段：状态转移
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)
        cur_state <= IDLE;
    else
        cur_state <= next_state;
end

// 第二阶段：状态转移判断
always @(*)begin
    next_state = IDLE;
    case (cur_state)
        IDLE: begin
            if(skip_en)
                next_state = PRE;
            else
                next_state = IDLE; 
        end  
        PRE: begin
            if(~error_en)
                if(skip_en)
                    next_state = SFD;
                else
                    next_state = PRE; 
            else
                next_state = ERR;
        end  
        SFD: begin
            if(~error_en)
                if(skip_en)
                    next_state = DES_MAC;
                else
                    next_state = SFD; 
            else
                next_state = ERR;
        end  
        DES_MAC: begin
            if(skip_en)
                next_state = SOURCE_MAC;
            else
                next_state = DES_MAC; 
        end  
        SOURCE_MAC: begin
            if(~error_en)
                if(skip_en)
                    next_state = TYPE;
                else
                    next_state = SOURCE_MAC; 
            else
                next_state = ERR;
        end  
        TYPE: begin
            if(skip_en)
                next_state = DATA;
            else
                next_state = TYPE; 
        end  
        DATA: begin
            if(~error_en)
                if(skip_en)
                    next_state = FCS;
                else
                    next_state = DATA; 
            else
                next_state = ERR;
        end  
        FCS:begin
            if(skip_en)
                next_state = IDLE;
            else
                next_state = FCS;
        end
        ERR:begin
            if(skip_en)
                next_state = IDLE;
            else
                next_state = ERR;
        end
    endcase
end

// 状态机第三段：状态输出
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)begin
        cnt                     <= 7'd0 ;
        skip_en                 <= 1'd0 ;
        error_en                <= 1'd0 ;
        eth_des_mac_t           <= 48'd0;
        type_t                  <= 16'd0;

        op_data                 <= 16'd0;
        data_source_mac_t       <= 48'd0;
        data_source_ip_t        <= 32'd0;
        data_des_ip_t           <= 32'd0;

        des_mac                 <= 47'd0;
        des_ip                  <= DSE_IP;
        rx_done                 <= 1'd0;
        type                    <= 1'd0;
    end
    else begin
        skip_en     <= 1'd0;
        rx_done <= 1'd0;
        case (next_state)   // 计时器放内部，逻辑更清晰，不依赖系统时钟的起始时间
            IDLE:begin      // 读取第一个55
                if(gmii_rx_en && gmii_rx_data == 8'h55)begin
                    cnt <= 7'd0;
                    skip_en <= 1'd1;
                end
            end
            PRE: begin // 读剩下6个55
                if(gmii_rx_en)begin
                    cnt <= cnt +7'd1;
                    if(cnt < 7'd6 && gmii_rx_data == 8'h55)begin
                        if(cnt == 7'd5)begin
                            cnt <= 7'd0;
                            skip_en <= 1'd1;
                        end
                    end
                    else
                        error_en <= 1'd1;
                end
                else
                    error_en <= 1'd1;
            end
            SFD:begin// 帧起始界定符 d5
                if(gmii_rx_en)
                    if(gmii_rx_data == 8'hd5)
                        skip_en <= 1'd1;
                    else
                        error_en <= 1'd1;
                else
                    error_en <= 1'd1;
            end
            DES_MAC:begin// 目标mac（接收的时候目标mac就是自己的mac）
                cnt <= cnt +7'd1;
                if(gmii_rx_en)begin
                    case (cnt)
                        7'd0: eth_des_mac_t[47:40]  <=  gmii_rx_data;
                        7'd1: eth_des_mac_t[39:32]  <=  gmii_rx_data;
                        7'd2: eth_des_mac_t[31:24]  <=  gmii_rx_data;
                        7'd3: eth_des_mac_t[23:16]  <=  gmii_rx_data;
                        7'd4: eth_des_mac_t[15:8]   <=  gmii_rx_data;
                        7'd5: begin
                            eth_des_mac_t[7:0]      <=  gmii_rx_data;
                            cnt     <= 7'd0;
                            skip_en <= 1'd1;
                        end
                    endcase
                end
                else
                    error_en <= 1'd1;
            end
            SOURCE_MAC: begin// 源mac（接收的时候源mac就是从机的mac）
                cnt <= cnt +7'd1;
                if(gmii_rx_en)
                    if(eth_des_mac_t == BOARD_MAC || eth_des_mac_t == 48'hff_ff_ff_ff_ff_ff)begin
                        if(cnt == 7'd5)begin
                            cnt     <= 7'd0;
                            skip_en <= 1'd1;
                        end
                    end
                    else
                        error_en <= 1'd1;
                else
                    error_en <= 1'd1;
            end
            TYPE:begin //当size_type_t > 0x0600时，表示该以太网的数据属于哪一个上层协议，比如，0x0800表示ip协议（网际协议），0x0806表示arp协议（地址解析协议）
                cnt <= cnt +7'd1;
                if(gmii_rx_en)
                    case (cnt)
                        7'd0: type_t[15:8] <= gmii_rx_data;
                        7'd1: begin
                            type_t[7:0 ]   <= gmii_rx_data;
                            cnt                 <= 7'd0;
                            skip_en             <= 1'd1;
                        end     
                    endcase
                else
                    error_en <= 1'd1;
            end
            DATA:begin  // 以太网数据，arp数据包在其中
                cnt <= cnt +7'd1;
                if(gmii_rx_en) // 判断是否是arp协议
                    if(type_t == 16'h0806)
                        case (cnt)
                            7'd6:op_data[15:8]              <= gmii_rx_data;
                            7'd7:op_data[7: 0]              <= gmii_rx_data;

                            7'd8: data_source_mac_t[47:40]  <= gmii_rx_data;
                            7'd9: data_source_mac_t[39:32]  <= gmii_rx_data;
                            7'd10:data_source_mac_t[31:24]  <= gmii_rx_data;
                            7'd11:data_source_mac_t[23:16]  <= gmii_rx_data;
                            7'd12:data_source_mac_t[15: 8]  <= gmii_rx_data;
                            7'd13:data_source_mac_t[ 7: 0]  <= gmii_rx_data;
                            7'd14:data_source_ip_t [31:24]  <= gmii_rx_data;
                            7'd15:data_source_ip_t [23:16]  <= gmii_rx_data;
                            7'd16:data_source_ip_t [15: 8]  <= gmii_rx_data;
                            7'd17:data_source_ip_t [ 7: 0]  <= gmii_rx_data;

                            7'd24:data_des_ip_t [31:24]     <= gmii_rx_data;
                            7'd25:data_des_ip_t [23:16]     <= gmii_rx_data;
                            7'd26:data_des_ip_t [15: 8]     <= gmii_rx_data;
                            7'd27:data_des_ip_t [7 :0 ]     <= gmii_rx_data;
                            7'd28:begin
                                if(data_des_ip_t != BOARD_IP)
                                    error_en <= 1'd1;
                                else begin
                                    des_mac  <= data_source_mac_t; // 源mac
                                    des_ip   <= data_source_ip_t ; // 源ip
                                    if(op_data == 16'd1 || op_data == 16'd2)
                                        if(op_data == 16'd1)
                                            type <= 1'd1;
                                        else
                                            type <= 1'd0;
                                end 
                            end 
                            7'd45: begin                            //18个填充字节
                                skip_en <= 1'd1;
                                cnt <= 7'd0;
                            end   
                        endcase
                    else
                        error_en <= 1'd1;
                else
                    error_en <= 1'd1;
            end
            FCS:begin
                cnt <= cnt + 7'd1;
                if (gmii_rx_en) 
                    if(cnt == 7'd3)begin
                        cnt <= 7'd0;
                        skip_en <= 1'd1;
                        rx_done <= 1'd1;
                    end
                else
                    error_en <= 1'd1;
            end
            ERR: begin
                if(~gmii_rx_en)begin
                    skip_en <= 1'd1;
                    error_en <= 1'd0;
                end
            end
        endcase
    end
end
endmodule
